
DEBUG = False

from amplitude.dict import Dict
from win32com import client as w32c
from lxml import etree
from .xml import XML
from .text import Text
import os, re

wdOpenFormats = {
    'Auto': 0,      'Word': 6,      'RTF': 3,
    'HTML': 7,      'XML': 8,       'ODT': 18,      
    'DOC': 1,       'DOCX': 9,      
    'Text': 5,      'TXT': 5,
}

wdSaveFormats = {
    'Word': 12,     'RTF': 6,       'Text': 7, # Encoded Text
    'HTML': 8,      'MHT': 9,       'FilteredHTML': 10,
    'XML': 11,      'PDF': 17,      'XPS': 18,      
    'ODT': 23,      'DOC': 0,       'DOCX': 12,
}

extensions = {
    'Word': '.docx',    'RTF': '.rtf',      'Text': '.txt',
    'HTML': '.html',    'MHT': '.mht',      'FilteredHTML': '.html',
    'XML': '.xml',      'PDF': '.pdf',      'XPS': '.xps',
    'ODT': '.odt',      'DOC': '.doc',      'TXT': '.txt',
    'DOCX': '.docx'
}

msoEncodings = {
    'Auto': 50001,      'ANSI': 1252,       'Latin-1': 28591,
    'UTF-8': 65001,     'ASCII': 20127,
}


def app():
    return w32c.Dispatch("Word.Application")

    
class Word(Dict):

    def __init__(self, fn, config=None, format='Auto', 
                confirm=False, encoding='UTF-8'):
        if config is not None:
            self.config = config
        self.fn = fn.replace("/", "\\")
        self.app = app()
        self.doc = self.app.Documents.Open(self.fn, ConfirmConversions=confirm, Format=wdOpenFormats[format], Encoding=msoEncodings[encoding])
    
    def save(self, fn=None, format=None, encoding='UTF-8', add_to_recent_files=False):
        fn = fn or self.fn
        format = format or "Word"
        fn = os.path.splitext(fn or self.fn)[0] + extensions[format]        
        if fn != self.fn:
            self.doc.SaveAs(fn, FileFormat=wdSaveFormats[format], Encoding=msoEncodings[encoding], AddToRecentFiles=add_to_recent_files)
        else:
            self.doc.Save()
        self.fn = fn
    
    def attach_template(self, dotpath):
        if os.path.exists(dotpath):
            self.doc.AttachedTemplate = dotpath.replace("/", "\\")

    def close(self, save=False):
        self.doc.Close(SaveChanges=save)

    def get_xml_string(self, xslfn=None, declaration=True, encoding='UTF-8'):
        xml = etree.fromstring(self.doc.Range().XML)
        if xslfn is not None:
            xslt = etree.XSLT(etree.parse(xslfn))
            xml = xslt(xml)
        t = etree.tostring(xml, encoding=encoding, xml_declaration=declaration)
        t = re.sub("&#13;", "\n", t)
        return t

    def write_html(self, fn=None, xslfn=None, encoding='UTF-8', declaration=False):
        outfn = fn or os.path.splitext(self.fn)[0]+'.html'
        xslfn = xslfn or os.path.join(os.path.dirname(__file__), 'xsl', 'word_to_html.xsl')
        self.write_xml(fn=outfn, xslfn=xslfn, encoding=encoding, declaration=declaration)
        doc = XML(outfn)
        # join adjacent spans of the same class
        for span in doc.root.xpath("//span"):
            parent = span.getparent()
            if span.get('class') in [None, '']:
                XML.replace_with_contents(span)
            else:
                nxt = span.getnext()
                while span.tail in [None, ''] and nxt is not None \
                and nxt.tag == 'span' and nxt.get('class') == span.get('class'):
                    span.text = (span.text or '') + (nxt.text or '')
                    span.tail = nxt.tail
                    parent.remove(nxt)
                    nxt = span.getnext()
        doc.write()

    def write_items(self, fn=None, typename="Item", product_name="Product", accept=True, delete_comments=True, unpack=False):
        fn = fn or os.path.splitext(self.fn)[0]+'.xml'
        xslfn1 = os.path.join(os.path.dirname(__file__), 'xsl', 'html_to_items.xsl')
        xslfn2 = os.path.join(os.path.dirname(__file__), 'xsl', 'word_to_items.xsl')
        if accept==True:
            self.accept_revisions()
        if delete_comments==True:
            self.rm_comments()
        self.write_html(fn=fn)
        doc = XML(fn, self.config)
        doc.root = doc.xslt(xslfn=xslfn1, xsl_params={'typename': typename, 'product_name': product_name}).getroot()
        doc.root = doc.xslt(xslfn=xslfn2, xsl_params={'typename': typename, 'product_name': product_name}).getroot()
        doc.write()
        doc = Text(doc.fn)
        doc.text = doc.text.replace("{{", "<").replace("}}", ">")
        doc.write()
        doc = XML(doc.fn, self.config)
        items = doc.root.find(".//items")
        if items is not None:
            doc.root = items
            doc.write()
        if unpack==True:
            doc.unpack_included_items()
            doc.write()

    def write_xml(self, fn=None, xslfn=None, encoding='UTF-8', declaration=True):
        outfn = fn or os.path.splitext(self.fn)[0] + '.xml'
        t = self.get_xml_string(xslfn=xslfn, encoding=encoding, declaration=declaration)
        f=open(outfn, 'wb'); f.write(t); f.close()

    def write_txt(self, fn=None, xslfn=None, encoding='UTF-8'):
        outfn = fn or os.path.splitext(self.fn)[0] + '.txt'
        t = self.get_xml_string(xslfn=xslfn, encoding=encoding)
        t = re.sub("<[^>]+>", '', t)
        f=open(outfn, 'wb'); f.write(t); f.close()
 
    def compare_from(self, fn, dest, author, config=None, template=None,
                    formatting=False, case=True, whitespace=False,
                    tables=True, headers=False, 
                    footnotes=True, textboxes=True, fields=True,
                    comments=True, moves=False, 
                    meta=True, codes=True, images=True):
        """compare changes from fn to self.fn"""
        config = config or self.config
        orig=Word(fn)
        self.app.CompareDocuments(
            OriginalDocument=orig.doc, 
            RevisedDocument=self.doc, 
            CompareFormatting=formatting, 
            CompareCaseChanges=case, 
            CompareWhitespace= whitespace, 
            CompareTables=tables, 
            CompareHeaders=headers, 
            CompareFootnotes=footnotes, 
            CompareTextboxes=textboxes, 
            CompareFields=fields, 
            CompareComments=comments, 
            CompareMoves=moves, 
            RevisedAuthor=author, 
            IgnoreAllComparisonWarnings=False)
        res = self.app.Documents(self.app.ActiveDocument())
        res.UpdateStylesOnOpen = True
        if config is not None:
            dotpath = os.path.join(config.Repository.path,  config.Repository.templates or 'templates', template or "archive-file.dotm")
            if os.path.exists(dotpath):
                res.AttachedTemplate = dotpath
        if meta != True:
            self.app.Run(MacroName='MetaNoRevisions')
        if codes != True:
            self.app.Run(MacroName='CodesNoRevisions')
        if images != True:
            self.app.Run(MacroName='ImagesNoRevisions')
        res.SaveAs(dest)
        res.Close()
        orig.close()

    def merge_from(self, origfn, destfn, origauthor, revauthor, template=None, 
                  config=None, formatting=False, case=True,
                  whitespace=False, tables=True, headers=False, 
                  footnotes=True, textboxes=True, fields=True, 
                  comments=True, moves=False, 
                  meta=True, codes=True, images=True):
        config = config or self.config
        orig = Word(origfn)
        rev = self
        self.app.MergeDocuments(
            OriginalDocument=orig.doc,
            RevisedDocument=rev.doc,
            CompareFormatting=formatting,
            CompareCaseChanges=case,
            CompareWhitespace=whitespace,
            CompareTables=tables,
            CompareHeaders=headers,
            CompareFootnotes=footnotes,
            CompareTextboxes=textboxes,
            CompareFields=fields,
            CompareComments=comments,
            CompareMoves=moves,
            OriginalAuthor=origauthor,
            RevisedAuthor=revauthor,
            #FormatFrom=wdMergeFormatFromPrompt
        )
        res = self.app.Documents(self.app.ActiveDocument())
        if config is not None:
            dotpath = os.path.join(config.Repository.path,  config.Repository.templates or 'templates', template or "archive-file.dotm")
            if os.path.exists(dotpath):
                res.AttachedTemplate = dotpath
        if meta != True:
            self.app.Run(MacroName='MetaNoRevisions')
        if codes != True:
            self.app.Run(MacroName='CodesNoRevisions')
        if images != True:
            self.app.Run(MacroName='ImagesNoRevisions')
        res.SaveAs(destfn)
        res.Close()
        orig.close()

    def accept_revisions(self): 
        self.app.Run(MacroName='AcceptAllChangesInDoc') # use the Word command

    def rm_comments(self):
        n = len(self.doc.Comments)
        for i in range(1, n+1): 
            self.doc.Comments(1).Delete()

    def run_macro(self, macro_name):
        self.app.Run(MacroName=macro_name)

WordFile = Word
